Django & Dropzone js
You can also be interested in:
Hi everybody, this one is just an example of how you can integrate Dropzone.js in your Django application. It's just a basic example, but probably a good starting point.

My scenario is a sort of two steps form: I have a Sale model which can have many images associated.
class Sale(models.Model):
title = models.CharField(_("titolo"), max_length=255)
# ...
class SaleImage(models.Model):
sale = models.ForeignKey(
Sale, verbose_name="vendita", related_name="images", on_delete=models.CASCADE
)
image = models.ImageField(_("immagine"), upload_to=sale_image_file_name)
# ...
The problem with Dropzone is that it tries to do everything automagically and that files are uploaded by ajax. That means that images are not uploaded when the user presses a submit button transmitting also other data (the ones we need to create the Sale object). Of course, we need a Sale instance before inserting images associated to it.
So I decided to split the form in two: first, the user inserts the Sale information, then after saving he can edit it and add images.
Another thing to manage is the presence of already uploaded images. I mean, this is an update view, so maybe we already have some images associated and I want them to be graphically displayed as the new inserted ones! For this reason, we'll customize a bit the Dropzone behaviour, attaching some callbacks to its managed events.
Let's start from the template.
{% block dropzone %}
<div class="dropzone mt-4 mb-4" id="dropzone">
<div class="images-preview" id="dropzone-images-preview">
{% for image in object.images.all %}
{% thumbnail image.image "120x120" crop="center" as im %}
<div class="thumb" data-id="{{ image.id }}">
<i class="fa fa-remove"></i>
<img class="img-thumbnail" src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}">
</div>
{% endthumbnail %}
{% endfor %}
</div>
<div class="spinner" id="dropzone-spinner"><i class="fa fa-spinner fa-spin"></i></div>
</div>
<script charset="utf-8">
(function ($) {
var thumbs = [];
var csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
Dropzone.autoDiscover = false;
var deleteImage = function () {
var thumb = $(this).parent('.thumb');
var id = thumb.attr('data-id');
$.get('{% url 'market:update-sale-images' object.id %}?image_id=' + id, function (data) {
if (data.status) {
thumb.remove();
}
})
}
$('#dropzone-images-preview .thumb i').on('click', deleteImage);
var myDropzone = new Dropzone(
"div#dropzone",
{
url: "{% url 'market:update-sale-images' object.id %}",
params: {'csrfmiddlewaretoken': csrftoken},
acceptedFiles: 'image/*',
addedfile: function (file) {
$('#dropzone-spinner').addClass('active');
},
},
complete: function () {
$('#dropzone-spinner').removeClass('active');
},
success: function (klass, data) {
thumbs.forEach(function (thumb) {
$('#dropzone-images-preview').append($('<div />', { class: 'thumb' })
.attr('data-id', data.id)
.append(
$('<img />', { class: 'img-thumbnail', src: thumb}),
$('<i />', { class: 'fa fa-remove' }).on('click', deleteImage)
))
})
thumbs = []
},
thumbnail: function (klass, dataUrl) {
thumbs.push(dataUrl);
return null;
}
}
);
})(jQuery)
</script>
{% endblock dropzone %}
Some considerations:
- Lines 2-14: this is the dropzone area. I've added an images preview div including all already associated images (thumbs with remove icon), and a spinner I'll use as undetermined progress feedback while uploading an image (I'll disable the default Dropzone behaviour because I need to manage images preview myself). There is room for improvements here: you can implement your custom progress bar to show upload progress.
- Lines 17-19: I create a thumbs array that will keep all updates images until they're not shown as preview images. I read the CSRF token (which is present in another part of the template) and I tell Dropzone to avoid auto discovering feature.
- Line 21-29: this is the dele image function, which performs an ajax request and removes the image thumb on success image deletion
- Line 31: I attach the previous function to the remove icon click events.
- Lines 33-63: my Dropzone configuration. When a file is added I activate the spinner overlay, which will be deactivated on complete. When the thumbnail is ready (L58) I push the dataUrl into the thumbs array. When the upload is successful I create a thumb for each item in the thumbs array and then I clean it. I also attach the deleteImage callback to the newly generated thumb.
Now let's see a bit of SCSS:
#dropzone {
align-items: center;
border: 4px dashed #eee !important;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
.dz-message {
margin: 2rem 0 0;
}
}
.spinner {
align-items: center;
background: rgba(255, 255, 255, .8);
bottom: 0;
display: none;
color: #999;
flex-direction: row;
font-size: 2rem;
left: 0;
justify-content: center;
position: absolute;
right: 0;
top: 0;
&.active {
display: flex;
padding: 1rem 0;
}
}
.images-preview {
align-items: center;
display: flex;
flex-direction: row;
justify-content: center;
.thumb {
margin: 0 .5rem;
position: relative;
img {
border-radius: 20px;
}
.fa {
align-items: center;
background: #000;
border-radius: 50%;
color: #fff;
cursor: pointer;
display: flex;
height: 25px;
justify-content: center;
opacity: .3;
position: absolute;
right: 15px;
top: 15px;
width: 25px;
&:hover {
opacity: 1;
}
}
}
}
And finally the django view:
class SaleImagesUpdateView(View):
# GET to delete an image
def get(self, request, pk):
try:
id = request.GET.get('image_id')
sale_image = get_object_or_404(SaleImage, id=id)
sale_image.delete()
data = {'status': True}
return JsonResponse(data)
except:
data = {'status': False}
return JsonResponse(data)
# POST to add an image
def post(self, request, pk):
sale = get_object_or_404(Sale, pk=pk)
try:
files = request.FILES.getlist('file')
for filename in files:
save_image = SaleImage(sale=sale, image=filename)
save_image.save()
data = {'status': True, 'id': save_image.id}
return JsonResponse(data)
except KeyError:
pass
data = {'status': False}
return JsonResponse(data)
That's all, bye!
Your Smartwatch Loves Tasker!
Your Smartwatch Loves Tasker!
Featured
Django admin and bootstrap 5
Bootstrap 5 has come, currently in beta release, and seems already very stable.
So the question is: are you looking for ...
About code optimization, learn from exercises
Let's see an example of exercise you can face during a job interview, and let's use it to understand some ...
Notes on the Pearson correlation coefficient
The Pearson correlation coefficient is a measure of the linear correlation between two variables X and Y. It has a ...
Archive
- 2021
- 2020
- 2019
- 2018
- 2017
- Nov
- Oct
- Aug
- Jun
- Mar
- Feb
- 2016
- Oct
- Jun
- May
- Apr
- Mar
- Feb
- Jan
- 2015
- Nov
- Oct
- Aug
- Apr
- Mar
- Feb
- Jan
- 2014
- Sep
- Jul
- May
- Apr
- Mar
- Feb
- Jan
- 2013
- Nov
- Oct
- Sep
- Aug
- Jul
- Jun
- May
- Apr
- Mar
- Feb
- Jan
- 2012
- Dec
- Nov
- Oct
- Aug
- Jul
- Jun
- May
- Apr
- Jan
- 2011
- Dec
- Nov
- Oct
- Sep
- Aug
- Jul
- Jun
- May